package com.adee.java;

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * 仿照 java.internal.ref.Cleaner 写一个利用虚引用自动回收资源的例子。
 * 假设需求：自动释放非堆内存，模仿DirectBuffer。
 * 实际DirectBuffer使用Cleaner来自动释放内存资源。
 */
public class Test038_PhantomReference {
    public static void main(String[] args) throws InterruptedException {
        MyDirectBuffer buffer = new MyDirectBuffer(16);
        System.out.println("睡眠5秒。。。");
        TimeUnit.SECONDS.sleep(5);
        buffer = null;
        System.gc();
        System.out.println("继续睡眠5秒。。。"); // MyCleaner线程自动释放内存
        TimeUnit.SECONDS.sleep(10);
    }
}

class MyDirectBuffer {
    private long address; // 内存起始地址
    private int size; // 内存大小
    public MyDirectBuffer(int size) {
        this.address = new Random().nextLong(); // 模拟Unsafe.allocate()
        this.size = size;
        System.out.printf("---> 分配内存，address=%d,size=%d \n", address, size);
        MyCleaner.create(this, ()->{
            System.out.printf("<--- 释放内存，address=%d,size=%d \n", address, size);
        });
    }

}

class MyCleaner extends PhantomReference<MyDirectBuffer> {
    public static ReferenceQueue<? super MyDirectBuffer> referenceQueue = new ReferenceQueue<>();
    public static MyCleaner first; // gc roots

    private MyCleaner prev, next;

    private Runnable runnable;

    private MyCleaner(MyDirectBuffer referent, ReferenceQueue<? super MyDirectBuffer> q) {
        super(referent, q);
    }

    // 给实际对象referent创建一个虚引用
    public static MyCleaner create(MyDirectBuffer referent, Runnable clean) {
        MyCleaner myCleaner = new MyCleaner(referent, referenceQueue);
        // 放入双向链表中，保证referent是"虚引用可达"的
        add(myCleaner);
        myCleaner.runnable = clean;
        return myCleaner;
    }

    // 放入双向链表中
    private static void add(MyCleaner myCleaner) {
        if(first == null) {
            first = myCleaner;
        } else {
            myCleaner.next = first;
            first.prev = myCleaner;
            first = myCleaner;
        }
    }

    // 从双向链表中删除
    private static boolean remove(MyCleaner myCleaner) {
        if(myCleaner == null || first == null) {
            return false;
        }
        if(myCleaner == first) {
            first = myCleaner.next;
            myCleaner.next.prev = null;
            myCleaner.next = null;
        }else {
            MyCleaner prev = myCleaner.prev;
            MyCleaner next = myCleaner.next;
            prev.next = next;
            if (next != null) {
                next.prev = prev;
            }
            myCleaner.next = null;
            myCleaner.prev = null;
        }
        return true;
    }

    private void clean() {
        remove(this);
        if(runnable != null) {
            runnable.run(); // 自定义清理逻辑
        }
    }

    static {
        Thread thread = new Thread(()->{
            for(;;) {
                try {
                    Reference<?> reference = referenceQueue.remove(300);
                    if(reference != null && reference instanceof MyCleaner) {
                        System.out.println("reference:" + reference);
                        ((MyCleaner)reference).clean();
                    }
                } catch (Throwable e) { // 任何异常都不能抛出，防止线程死掉
                    e.printStackTrace();
                }
            }
        });
        thread.setDaemon(true);
        thread.start();
    }
}
